1   /*
2    * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  /* @test
27   @summary Test RealTime-tunings using SoftReciver.send method */
28  
29  import java.io.IOException;
30  
31  import javax.sound.midi.*;
32  import javax.sound.sampled.*;
33  
34  import com.sun.media.sound.*;
35  
36  public class RealTimeTuning {
37  
38      private static class PitchSpy {
39          public float pitch = 0;
40  
41          public Soundbank getSoundBank() {
42              ModelOscillator osc = new ModelOscillator() {
43                  public float getAttenuation() {
44                      return 0;
45                  }
46  
47                  public int getChannels() {
48                      return 0;
49                  }
50  
51                  public ModelOscillatorStream open(float samplerate) {
52                      return new ModelOscillatorStream() {
53                          public void close() throws IOException {
54                              pitch = 0;
55                          }
56  
57                          public void noteOff(int velocity) {
58                              pitch = 0;
59                          }
60  
61                          public void noteOn(MidiChannel channel,
62                                  VoiceStatus voice, int noteNumber, int velocity) {
63                              pitch = noteNumber * 100;
64                          }
65  
66                          public int read(float[][] buffer, int offset, int len)
67                                  throws IOException {
68                              return len;
69                          }
70  
71                          public void setPitch(float ipitch) {
72                              pitch = ipitch;
73                          }
74                      };
75                  }
76              };
77              ModelPerformer performer = new ModelPerformer();
78              performer.getOscillators().add(osc);
79              SimpleInstrument testinstrument = new SimpleInstrument();
80              testinstrument.setPatch(new Patch(0, 0));
81              testinstrument.add(performer);
82              SimpleSoundbank testsoundbank = new SimpleSoundbank();
83              testsoundbank.addInstrument(testinstrument);
84              return testsoundbank;
85          }
86      }
87  
88      public static void sendTuningChange(Receiver recv, int channel,
89              int tuningpreset, int tuningbank) throws InvalidMidiDataException {
90          // Data Entry
91          ShortMessage sm1 = new ShortMessage();
92          sm1.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 04);
93          ShortMessage sm2 = new ShortMessage();
94          sm2.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
95  
96          // Tuning Bank
97          ShortMessage sm3 = new ShortMessage();
98          sm3.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06, tuningbank);
99          // Data Increment
100         ShortMessage sm4 = new ShortMessage();
101         sm4.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
102         // Data Decrement
103         ShortMessage sm5 = new ShortMessage();
104         sm5.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
105 
106         // Data Entry
107         ShortMessage sm6 = new ShortMessage();
108         sm6.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 03);
109         ShortMessage sm7 = new ShortMessage();
110         sm7.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
111 
112         // Tuning program
113         ShortMessage sm8 = new ShortMessage();
114         sm8
115                 .setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06,
116                         tuningpreset);
117         // Data Increment
118         ShortMessage sm9 = new ShortMessage();
119         sm9.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
120         // Data Decrement
121         ShortMessage sm10 = new ShortMessage();
122         sm10.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
123 
124         recv.send(sm1, -1);
125         recv.send(sm2, -1);
126         recv.send(sm3, -1);
127         recv.send(sm4, -1);
128         recv.send(sm5, -1);
129         recv.send(sm6, -1);
130         recv.send(sm7, -1);
131         recv.send(sm8, -1);
132         recv.send(sm9, -1);
133         recv.send(sm10, -1);
134 
135     }
136 
137     private static void assertTrue(boolean value) throws Exception {
138         if (!value)
139             throw new RuntimeException("assertTrue fails!");
140     }
141 
142     public static void testTunings(int[] msg, int tuningProgram,
143             int tuningBank, int targetNote, float targetPitch, boolean realtime)
144             throws Exception {
145         AudioSynthesizer synth = new SoftSynthesizer();
146         AudioInputStream stream = synth.openStream(null, null);
147         Receiver recv = synth.getReceiver();
148         MidiChannel channel = synth.getChannels()[0];
149         byte[] buff = new byte[2048];
150 
151         // Create test instrument which we can use to monitor pitch changes
152         PitchSpy pitchspy = new PitchSpy();
153 
154         synth.unloadAllInstruments(synth.getDefaultSoundbank());
155         synth.loadAllInstruments(pitchspy.getSoundBank());
156 
157         SysexMessage sysex = null;
158 
159         // Send tuning changes
160         if (msg != null) {
161             byte[] bmsg = new byte[msg.length];
162             for (int i = 0; i < bmsg.length; i++)
163                 bmsg[i] = (byte) msg[i];
164             sysex = new SysexMessage();
165             sysex.setMessage(bmsg, bmsg.length);
166             if (targetPitch == 0) {
167                 targetPitch = (float) new SoftTuning(bmsg)
168                         .getTuning(targetNote);
169                 // Check if targetPitch != targetNote * 100
170                 assertTrue(Math.abs(targetPitch - targetNote * 100.0) > 0.001);
171             }
172         }
173 
174         if (tuningProgram != -1)
175             sendTuningChange(recv, 0, tuningProgram, tuningBank);
176 
177         // First test without tunings
178         channel.noteOn(targetNote, 64);
179         stream.read(buff, 0, buff.length);
180         assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
181 
182         // Test if realtime/non-realtime works
183         if (sysex != null)
184             recv.send(sysex, -1);
185         stream.read(buff, 0, buff.length);
186         if (realtime)
187             assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
188         else
189             assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
190 
191         // Test if tunings works
192         channel.noteOn(targetNote, 0);
193         stream.read(buff, 0, buff.length);
194         assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
195 
196         channel.noteOn(targetNote, 64);
197         stream.read(buff, 0, buff.length);
198         assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
199 
200         channel.noteOn(targetNote, 0);
201         stream.read(buff, 0, buff.length);
202         assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
203 
204         stream.close();
205     }
206 
207     public static void main(String[] args) throws Exception {
208         // Test with no-tunings
209         testTunings(null, -1, -1, 60, 6000, false);
210 
211         int[] msg;
212         // 0x02 SINGLE NOTE TUNING CHANGE (REAL-TIME)
213         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x02, 0x10, 0x02, 36, 36, 64,
214                 0, 60, 70, 0, 0, 0xf7 };
215         testTunings(msg, 0x10, 0, 60, 7000, true);
216 
217         // 0x07 SINGLE NOTE TUNING CHANGE (NON REAL-TIME) (BANK)
218         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
219                 36, 64, 0, 60, 80, 0, 0, 0xf7 };
220         testTunings(msg, 0x07, 0x05, 60, 8000, false);
221 
222         // 0x07 SINGLE NOTE TUNING CHANGE (REAL-TIME) (BANK)
223         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
224                 36, 64, 0, 60, 80, 0, 0, 0xf7 };
225         testTunings(msg, 0x07, 0x05, 60, 8000, true);
226 
227         // 0x08 scale/octave tuning 1-byte form (Non Real-Time)
228         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
229                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
230         testTunings(msg, -1, -1, 60, 0, false);
231 
232         // 0x08 scale/octave tuning 1-byte form (REAL-TIME)
233         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
234                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
235         testTunings(msg, -1, -1, 60, 0, true);
236 
237         // 0x09 scale/octave tuning 2-byte form (Non Real-Time)
238         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
239                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
240                 30, 35, 40, 45, 50, 51, 52, 0xf7 };
241         testTunings(msg, -1, -1, 60, 0, false);
242 
243         // 0x09 scale/octave tuning 2-byte form (REAL-TIME)
244         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
245                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
246                 30, 35, 40, 45, 50, 51, 52, 0xf7 };
247         testTunings(msg, -1, -1, 60, 0, true);
248 
249     }
250 }